#include "D3D9Renderer.h"
#include "VertexBufferManager.h"
#include "IndexBuffer.h"
#include "ShaderManager.h"
#include "GBuffer.h"

#include <dxgi.h>
#include <fstream>

using std::ofstream;
using std::ifstream;
using std::ios_base;

#ifdef _DEBUG
#include "../Utility/Misc/DebugOutput.h";
#include<sstream>
using std::stringstream;
#endif

IDirect3D9*			D3D9Renderer::d3d			= NULL;
IDirect3DDevice9*	D3D9Renderer::device		= NULL;
ID3DXEffectPool*	D3D9Renderer::effectPool	= NULL;

bool				D3D9Renderer::vsync;
int					D3D9Renderer::videoCardMemory;
char				D3D9Renderer::videoCardDescription[128];

bool D3D9Renderer::Initialize(HWND _hwnd, bool _fullscreen, bool _vsync, int _horizontalRes, int _verticalRes)
{
	HRESULT result;
	D3DFORMAT format = D3DFMT_A8R8G8B8;

	d3d = Direct3DCreate9(D3D_SDK_VERSION);
	
	result = d3d->CheckDeviceType(	D3DADAPTER_DEFAULT,
									D3DDEVTYPE_HAL,
									format,
									format,
									!_fullscreen);

	D3DADAPTER_IDENTIFIER9 adapterInfo;
	result = d3d->GetAdapterIdentifier(	D3DADAPTER_DEFAULT,
								0,
								&adapterInfo);

	if(AdapterSupportInfo())
	{
		#ifdef _DEBUG
		stringstream adapterDetails;
		adapterDetails	<< "GPU: \nNAME: " << videoCardDescription << " :: RAM: " << videoCardMemory << "Mb";
		DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::INFO, adapterDetails.str().c_str());
		#endif
	}
	else
	{
		return false;
	}


	if(result<0)
	{
		#ifdef _DEBUG
		DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::FAILURE_CREATE ,"BACK BUFFER FORMAT UNAVAILABLE");

		#endif

		return false;
	}


	D3DPRESENT_PARAMETERS pp;
	ZeroMemory(&pp, sizeof(D3DPRESENT_PARAMETERS));

	pp.BackBufferCount = 1;
	pp.BackBufferWidth   = _horizontalRes;
    pp.BackBufferHeight  = _verticalRes;
	pp.BackBufferFormat = format;

	pp.MultiSampleType=D3DMULTISAMPLE_NONE;
	pp.MultiSampleQuality=0;

	pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	pp.hDeviceWindow = _hwnd;
	pp.Windowed = !_fullscreen;
	pp.FullScreen_RefreshRateInHz=D3DPRESENT_RATE_DEFAULT;
	pp.PresentationInterval		= (_vsync) ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;

	pp.EnableAutoDepthStencil = true;
	pp.AutoDepthStencilFormat = D3DFMT_D24S8;

	pp.Flags = 0;

	if(!d3d)
	{
		#ifdef _DEBUG
		DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::FAILURE_CREATE ,"DIRECT3D9 COM OBJECT");
		#endif

		return false;
	}

	#ifdef _DEBUG
	DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::SUCCESS_CREATE ,"DIRECT3D9 COM OBJECT");
	#endif

	result = d3d->CreateDevice(0, D3DDEVTYPE_HAL, pp.hDeviceWindow, D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, &device);

	if(result<0)
	{
		#ifdef _DEBUG
		DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::FAILURE_CREATE ,"DIRECT3D9 DEVICE");
		#endif

		return false;
	}

	#ifdef _DEBUG
	DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::SUCCESS_CREATE ,"DIRECT3D9 DEVICE");
	#endif

	result = D3DXCreateEffectPool(&effectPool);

	if(result<0)
	{
		#ifdef _DEBUG
		DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::FAILURE_CREATE ,"DIRECT3D9 EFFECT POOL");
		#endif

		return false;
	}

	#ifdef _DEBUG
	DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::SUCCESS_CREATE ,"DIRECT3D9 EFFECT POOL");
	#endif

	VertexBufferManager::Initialize();
	ShaderManager::Initialize();
	//GBuffer::Initialize(_horizontalRes, _verticalRes);

	return true;
}

bool D3D9Renderer::AdapterSupportInfo()
{
	HRESULT result;
	IDXGIFactory* factory;
	IDXGIAdapter* adapter;
	IDXGIOutput* adapterOutput;
	unsigned int numModes, i, stringLength;
	DXGI_MODE_DESC* displayModeList;
	DXGI_ADAPTER_DESC adapterDesc;
	int error;

	result = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory);
	if(FAILED(result))
		return false;

	result = factory->EnumAdapters(0, &adapter);
	if(FAILED(result))
		return false;

	result = adapter->EnumOutputs(0, &adapterOutput);
	if(FAILED(result))
		return false;

	result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL);
	if(FAILED(result))
		return false;

	displayModeList = new DXGI_MODE_DESC[numModes];
	if(!displayModeList)
		return false;

	result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, displayModeList);
	if(FAILED(result))
		return false;

	ofstream adapterOut;

	adapterOut.open("Res/Data/adapter.bin", ios_base::beg | ios_base::binary);

	for(i=0; i<numModes; i++)
	{
		if(displayModeList[i].Scaling == 0)
		{
			adapterOut.write((const char* )&displayModeList[i].Width, 4);
			adapterOut.write((const char* )&displayModeList[i].Height, 4);
			adapterOut.write((const char* )&displayModeList[i].RefreshRate.Numerator, 4);
		}
	}

	adapterOut.close();

	result = adapter->GetDesc(&adapterDesc);
	if(FAILED(result))
		return false;

	videoCardMemory = (int)(adapterDesc.DedicatedVideoMemory / 1024 / 1024);

	error = wcstombs_s(&stringLength, videoCardDescription, 128, adapterDesc.Description, 128);
	if(error != 0)
		return false;

	delete [] displayModeList;
	displayModeList = 0;

	adapterOutput->Release();
	adapterOutput = 0;

	adapter->Release();
	adapter = 0;

	factory->Release();
	factory = 0;

	return true;
}

void D3D9Renderer::Clear(DWORD _count, const D3DRECT* _rectsptr, DWORD _flags, D3DCOLOR _clearColor, float _clearDepth, DWORD _clearStencil)
{
	device->Clear(_count, _rectsptr, _flags, _clearColor, _clearDepth, _clearStencil);
}

void D3D9Renderer::BeginScene()
{
	device->BeginScene();
}

void D3D9Renderer::EndScene()
{
	device->EndScene();
}

void D3D9Renderer::RenderGame()
{
	GBuffer::Render();
}

void D3D9Renderer::Present(const RECT* _sourceRectPtr, const RECT* _destRectPtr, HWND _hDestWindow, const RGNDATA* _dirtyRegionPtr)
{
	device->Present(_sourceRectPtr, _destRectPtr, _hDestWindow, _dirtyRegionPtr);
}

void D3D9Renderer::Shutdown()
{
	IndexBuffer::Shutdown();
	ShaderManager::Shutdown();

	if(effectPool)
	{
		effectPool->Release();
		effectPool = 0;
	}
	if (device != NULL)
    {
		device->Release();	
        device = NULL;
    }
    if (d3d != NULL)
    {
        d3d->Release();	
        d3d = NULL;
	}
}